//
// @Author: Zhenwei Xiao
// @Description:
// @Version: 1.0.0
// @Date: 2021/4/7 下午5:07
// @Copyright: MIN-Group；国家重大科技基础设施——未来网络北大实验室；深圳市信息论与未来网络重点实验室
//

const transport = require("./Transport")
const Tlv = require("../encoding/TLV")

class StreamTransport extends transport.Transport{
    constructor(m) {
        super(m)
        this.conn = null
        this.recvBuf = null
        this.recvLen = null
    }
    // Transport
    // conn    net.Conn
    // recvBuf []byte // 数据接收缓冲区
    // recvLen uint64 // 当前数据接收缓冲区中的有效数据的长度

    //
    // @Description:
    // @receiver t
    //
    Close() {
        try {
            this.conn[0].disconnect()
            this.conn[1].disconnect()
        }catch (err){
            // log.Println(err)
            throw err
        }
    }

    //
    // @Description: 将lpPacket对象编码成字节数组后，通过流式通道发送出去
    // @receiver t
    // @param lpPacket  *packet.LpPacket
    // @return error
    //
    Send(lpPacket) {
        var encodeBuf = this.encodeLpPacket2ByteArray(lpPacket)
        if (encodeBuf[0] <= 0) {
            return new Error("encode LpPacket to array error")
        }
        var writeLen = 0
        var writeRet
        while (writeLen < encodeBuf[0]) {
            try{
                // console.log("encodeBuf[1].slice(0,encodeBuf[0])", Buffer.from(encodeBuf[1].slice(0,encodeBuf[0])))
                // console.log("conn", this.conn)
                writeRet = Buffer.from(encodeBuf[1].slice(0,encodeBuf[0])).length
                this.conn[0].send(Buffer.from(encodeBuf[1].slice(0,encodeBuf[0])))
            }catch (err){
                // log.Println("send to tcp transport error")
                // Q?
                // this.linkService.logicFace.Shutdown()
                return err
            }
            writeLen += writeRet
        }
        return null
    }

    //
    // @Description: 从接收缓冲区中读取包并调用linkService的ReceivePacket函数处理包
    //			每次收到数据时会调用这个函数从数据接收缓冲区中尝试读出一个LpPacket包。
    //			工作流程：
    //			（1） 首先，如果数据缓冲区中收到的字节数不足以构成了一个TLV的Type字段，则返回 nil,0 表示还需要等待数据接收
    //			（2） 如果解析出来的Type值不等于TlvLpPacket， 表示接收的数据出错了，需要提示调用者关闭Face
    //			（3） 如果接收到的数据还小于一个LpPacket的最小长度（不含负载的只含头部的长度）， 表示还需要等待数据接收
    //			（4） 如果长度足够，TLV的Length部分，并计算一个LpPacket的总长度 totalPktLen
    //			（5） 如果接收到的数据长度小于 totalPktLen ， 则还需要等待后面数据接收
    //			（6） 从[]byte中解析出LpPacket，并调用linkService.ReceivePacket(lpPacket) 处理一个完整的LpPacket包
    // @receiver t
    // @param buf []byte	接收缓冲区分片
    // @param bufLen uint64
    // @return error	错误信息
    // @return uint64	读取包的长度
    // @return *packet.LpPacket
    //
    readPktAndDeal(buf, bufLen) {
        // 如果接收到的数据长度小于 LpPacket type 字段的长度 3字节 则要等待
        if (bufLen < BigInt(Tlv.sizeOfVarNumber(Tlv.ComponentType.TlvLpPacket))) {
            return [null, 0, null]
        }
        var pktType
        try {
            pktType = Tlv.readVarNumber(buf.buffer, 0)
        }catch (err){
            // log.Println(err)
            return [err, 0, null]
        }
        console.log("pktType" , pktType)
        // 如果数据类型的 TLV 和 type值不等于   encoding.TlvLpPacket， 则接收出错，应该关闭当前logicFace
        if (pktType != Tlv.ComponentType.TlvLpPacket) {
            // log.Println("receive error pkt")
            return [new Error("receive lpPacket from tcp type error"), 0, null]
        }
        // 如果接收到的数据长度小于 LpPacket 的小于长度 则要等待
        // Q?
        if (bufLen < BigInt(this.linkService.lpPacketHeadSize)) {
            return [null, 0, null]
        }
        var pktTypeLen = Tlv.sizeOfVarNumber(pktType)
        // Q?
        // vlint 已取消定义 正常传number 大于32位传bigint
        // var pktLen = Tlv.readVarNumber(buf, Tlv.VlInt(pktTypeLen))
        var pktLen = Tlv.readVarNumber(buf.buffer, pktTypeLen)
        var totalPktLen = BigInt(pktTypeLen) + BigInt(Tlv.sizeOfVarNumber(pktLen)) + BigInt(pktLen)
        if (bufLen >= totalPktLen) {
            var lpPacket
            try {
                lpPacket = this.parseByteArray2LpPacket(buf.slice(0, Number(totalPktLen)))
            }catch (err){
                // log.Println("parse lpPacket error")
                return [null, totalPktLen, null]
            }
            return [null, totalPktLen, lpPacket]
        }
        return [null, 0, null]
    }

    //
    // @Description: 接收到数据后，处理包
    //			（1） 调用 readPktAndDeal ，传入当前接收的到数据的[]byte，以及接收到的数据长度
    //			（2） 如果 readPktAndDeal 返回错误，则将错误抛给上层调用者
    //			（3） 如果 readPktAndDeal 没返回错误，且返回的已经被处理的LpPacket长度pktLen大于0, 则循环做以下操作
    //					a） 统计已经处理的数据长度dealLen（等于每次处理包长度的总和），如果已经处理的长度小接收数据长度t.recvLen，
    //					而且 readPktAndDeal返回的错误为nil，且返回的pktLen > 0 ， 再次调用readPktAndDeal去处理数据
    //					b） 如果循环中readPktAndDeal返回的错误不为nil，则终止循环，并将错误报给调用者
    //			（4） 如果统计到的总处理长度 dealLen 大小0, 则将已经处理的数据从数据接收缓冲区中删除。删除的方法是将recvBuf[dealLen:t.recvLen]
    //				移到 t.recvBuf[:] ， 即将未处理的数据移到接收缓冲区开关，并将接收数据长度t.recvLen 送去 已经处理的长度 dealLen
    // @receiver t
    // @return *packet.LpPacket
    // @return error	如果处理包出错，则返回错误信息
    //
    doReceive() {
        var res = this.readPktAndDeal(this.recvBuf.slice(0,this.recvLen), this.recvLen)
        console.log("dr:", res)
        if (res[0] != null) {
            return [null, res[0]]
        }
        // 循环多次尝试从接收缓冲区中读出包并处理
        if (res[1] > 0) {
            // console.log("rb:", this.recvBuf)
            let buf = Buffer.from(this.recvBuf).slice(Number(res[1]),this.recvLen)
            // console.log("buf1111:", buf)
            buf.copy(Buffer.from(this.recvBuf))
            // console.log("buf2222:", this.recvBuf)
            this.recvLen -= Number(res[1])
        }
        var lpPacket = res[2]
        return [lpPacket, null]
    }

    //
    // @Description:  用协程调用，不断地从流式通道中读出数据
    //			（1） 从流式通道中读出数据，如果读出错，则关闭face
    //			（2） 如果读到数据，则调用onReceive尝试处理接收到的数据
    //			（3） 如果数据处理出错， 则关闭face
    // @receiver t
    // @return *packet.LpPacket, error
    //
    Receive() {
        while (true){
            if (this.recvBuf != null){
                var lpPacket = this.doReceive()
                console.log("lpPacket:", lpPacket)
                if (lpPacket[1] != null) {
                    return [null, lpPacket[1]]
                }
                if (lpPacket[0] != null) {
                    return [lpPacket[0], null]
                }
                console.log("lpPacket", lpPacket)
            }
            var recvRet, got
            try {
                console.log("this conn :", this.conn)
                got = this.conn[1].receive()
                console.log("got:", got)
                got.copy(this.recvBuf, this.recvLen)
                recvRet = got.length
            }catch (err){
               return [null, err]
            }
            this.recvLen += recvRet
        }
        return [null, null]
    }

    //
    // @Description: 	设置读超时时间
    // @receiver t
    // @param duration int64 <= 0 时表示永不超时
    // @return error
    //
    SetReadTimeout(duration) {
        console.log("this.conn:", this.conn)
        setTimeout(this.conn.disconnect(), duration)
        // if (duration <= 0) {
        //     return this.conn.setKeepAlive(1)
        // }
        // var curTime = time.Now()
        // return this.conn.setTimeout(curTime.Add(time.Duration(duration)))
    }
}

module.exports = {
    StreamTransport: StreamTransport
}



